<!DOCTYPE html>
            
<HTML>
<HEAD>
<meta name="booktitle" content="Developing Applications With Objective Caml" >
 <meta charset="ISO-8859-1"><meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<META name="GENERATOR" content="hevea 1.05-7 of 2000-02-24">
<META NAME="Author" CONTENT="Christian.Queinnec@lip6.fr">
<LINK rel=stylesheet type="text/css" href="videoc-ocda.css">
<script language="JavaScript" src="videoc.js"><!--
//--></script>
<TITLE>
 Database queries
</TITLE>
</HEAD>
<BODY class="regularBody">
<A HREF="book-ora056.html"><IMG SRC ="previous_motif.gif" ALT="Previous"></A>
<A HREF="index.html"><IMG SRC ="contents_motif.gif" ALT="Contents"></A>
<A HREF="book-ora058.html"><IMG SRC ="next_motif.gif" ALT="Next"></A>
<HR>

<H2> Database queries</H2>
<A NAME="sec-gestassoc"></A>
The implementation of a database, its interface, and its query
language is a project far too ambitious for the scope of this book and
for the Objective CAML knowledge of the reader at this point. However,
restricting the problem and using the functional programming style 
at its best allows us to create an interesting tool for query
processing. For instance, we show how to use iterators as well as
partial application to formulate and execute queries. We also show the
use of a data type encapsulating functional values.<BR>
<BR>
For this application, we use as an example a database on the members
of an association. It is presumed to be stored in the file 
<TT>association.dat</TT>.<BR>
<BR>
<A NAME="toc72"></A>
<H3> Data format</H3><A NAME="format-don"></A>
Most database programs use a ``proprietary'' format to store
the data they manipulate. However, it is usually possible to store the
data as some text that has the following structure:
<UL>
<LI>
 the database is a list of <EM>cards</EM> separated by
carriage-returns;

<LI> each card is a list of <EM>fields</EM> separated by some given
character, <CODE>':'</CODE> in our case;

<LI> a field is a string which contains no carriage-return nor the
 character <CODE>':'</CODE>;

<LI> the first card is the list of the names associated with the
fields, separated by the character <CODE>'|'</CODE>.
</UL>The association data file starts with:
<PRE>
Num|Lastname|Firstname|Address|Tel|Email|Pref|Date|Amount
0:Chailloux:Emmanuel:Universit� P6:0144274427:ec@lip6.fr:email:25.12.1998:100.00
1:Manoury:Pascal:Laboratoire PPS::pm@lip6.fr:mail:03.03.1997:150.00
2:Pagano:Bruno:Cristal:0139633963::mail:25.12.1998:150.00
3:Baro:Sylvain::0144274427:baro@pps.fr:email:01.03.1999:50.00
</PRE>The meaning of the fields is the following:
<UL>
<LI>
 <TT>Num</TT> is the member number;

<LI> <TT>Lastname</TT>, <TT>Firstname</TT>, <TT>Address</TT>, <TT>Tel</TT>, and <TT>Email</TT>
are obvious;

<LI> <TT>Pref</TT> indicates the means by which the member wishes to be
 contacted: by mail (<TT>mail</TT>), by email (<TT>email</TT>), or by
 phone (<TT>tel</TT>);

<LI> <TT>Date</TT> and <TT>Amount</TT> are the date and the amount of the last
 membership fee received, respectively.
</UL>We need to decide what represention the program should use internally
for a database. We could use either a list of cards
or an array of cards. On the one hand, a list has the nice property of
being easily modified: adding and removing a card are simple operations.
On the other hand, an array allows constant access time to any card.
Since our goal is to work on all the cards and not on some of them,
each query accesses all the cards. Thus a list is a good choice. The
same issue arises concerning the cards themselves: should they be
lists or arrays of strings? This time an array is a good choice,
since the format of a card is fixed for the whole database. It not
possible to add a new field. Since a query might access only a
few fields, it is important for this access to be fast.<BR>
<BR>
The most natural solution for a card would be to use an array indexed
by the names of the fields. Since such a type is not available in
Objective CAML, we can use an array (indexed by integers) and a
function associating a field name with the array index
corresponding to the field.


<PRE><BR># <B>type</B><CODE> </CODE>data_card<CODE> </CODE><CODE>=</CODE><CODE> </CODE>string<CODE> </CODE>array<CODE> </CODE>;;<BR># <B>type</B><CODE> </CODE>data_base<CODE> </CODE><CODE>=</CODE><CODE> </CODE>{<CODE> </CODE>card_index<CODE> </CODE><CODE>:</CODE><CODE> </CODE>string<CODE> </CODE>-&gt;<CODE> </CODE>int<CODE> </CODE>;<CODE> </CODE>data<CODE> </CODE><CODE>:</CODE><CODE> </CODE>data_card<CODE> </CODE>list<CODE> </CODE>}<CODE> </CODE>;;<BR>

</PRE>
 <BR>
<BR>
Access to the field named <TT>n</TT> of a card <TT>dc</TT> of
the database <TT>db</TT> is implemented by the function:


<PRE><BR># <B>let</B><CODE> </CODE>field<CODE> </CODE>db<CODE> </CODE>n<CODE> </CODE><TT>(</TT>dc<CODE> </CODE><CODE>:</CODE><CODE> </CODE>data_card<TT>)</TT><CODE> </CODE><CODE>=</CODE><CODE> </CODE>dc<CODE>.</CODE><TT>(</TT>db<CODE>.</CODE>card_index<CODE> </CODE>n<TT>)</TT><CODE> </CODE>;;<BR><CODE>val field : data_base -&gt; string -&gt; data_card -&gt; string = &lt;fun&gt;</CODE><BR>

</PRE>

The type of <TT>dc</TT> has been set to <I>data_card</I> to
constrain the function <TT>field</TT> to only accept string arrays and
not arrays of other types.<BR>
<BR>
Here is a small example:


<PRE><BR># <B>let</B><CODE> </CODE>base_ex<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>{<CODE> </CODE>data<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE>[</CODE><CODE> </CODE><CODE>[|</CODE><CODE>"Chailloux"</CODE>;<CODE> </CODE><CODE>"Emmanuel"</CODE><CODE>|]</CODE><CODE> </CODE>;<CODE> </CODE><CODE>[|</CODE><CODE>"Manoury"</CODE>;<CODE> </CODE><CODE>"Pascal"</CODE><CODE>|]</CODE><CODE> </CODE><CODE>]</CODE><CODE> </CODE><CODE> </CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>card_index<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>function</B><CODE> </CODE><CODE>"Lastname"</CODE>-&gt;<CODE>0</CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>"Firstname"</CODE>-&gt;<CODE>1</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_-&gt;</CODE>raise<CODE> </CODE>Not_found<CODE> </CODE><CODE> </CODE>}<CODE> </CODE>;;<BR><CODE>val base_ex : data_base =</CODE><BR><CODE>  {card_index=&lt;fun&gt;;</CODE><BR><CODE>   data=[[|"Chailloux"; "Emmanuel"|]; [|"Manoury"; "Pascal"|]]}</CODE><BR># List.map<CODE> </CODE><TT>(</TT>field<CODE> </CODE>base_ex<CODE> </CODE><CODE>"Lastname"</CODE><TT>)</TT><CODE> </CODE>base_ex<CODE>.</CODE>data<CODE> </CODE>;;<BR><CODE>- : string list = ["Chailloux"; "Manoury"]</CODE><BR>

</PRE>
<BR>
<BR>
The expression field<CODE> </CODE>base_ex<CODE> </CODE><CODE>"Lastname"</CODE> evaluates to a function
which takes a card and returns the value of its
<CODE>"Lastname"</CODE> field. The library function <TT>List.map</TT> applies the
function to each card of the database <TT>base_ex</TT>, and returns
the list of the results: a list of the
<CODE>"Lastname"</CODE> fields of the database.<BR>
<BR>
This example shows how we wish to use the functional style in our
program. Here, the partial application of <TT>field</TT> allows us to
define an access function for a given field, which we can use on any
number of cards. This also shows us that the implementation of the
<TT>field</TT> function is not very efficient, since although we are
always accessing the same field, its index is computed for each
access. The following implementation is better:


<PRE><BR># <B>let</B><CODE> </CODE>field<CODE> </CODE>base<CODE> </CODE>name<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><CODE> </CODE>base<CODE>.</CODE>card_index<CODE> </CODE>name<CODE> </CODE><B>in</B><CODE> </CODE><B>fun</B><CODE> </CODE><TT>(</TT>card<CODE> </CODE><CODE>:</CODE><CODE> </CODE>data_card<TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE>card<CODE>.</CODE><TT>(</TT>i<TT>)</TT><CODE> </CODE>;;<BR><CODE>val field : data_base -&gt; string -&gt; data_card -&gt; string = &lt;fun&gt;</CODE><BR>

</PRE>

Here, after applying the function to two arguments, the index of the
field is computed and is used for any subsequent application.<BR>
<BR>
<A NAME="toc73"></A>
<H3> Reading a database from a file</H3>As seen from Objective CAML, a file containing a database is just a list of
lines. The first work that needs to be done is to read each line as a
string, split it into smaller parts according to the separating
character, and then extract the corresponding data as well as the field
indexing function.<BR>
<BR>

<H4> Tools for processing a line</H4>
We need a function <TT>split</TT> that splits a string at every occurrence of
some separating character. This function uses the function
<TT>suffix</TT> which returns the suffix of a string <TT>s</TT> after
some position <TT>i</TT>. To do this, we use three
predefined functions:
<UL>
<LI>
 <TT>String.length</TT> returns the length of a string;

<LI> <TT>String.sub</TT> returns the substring of <TT>s</TT>
 starting at position <TT>i</TT> and of length <TT>l</TT>;

<LI> <TT>String.index_from</TT> computes the position of the first occurrence
 of character <TT>c</TT> in the string <TT>s</TT>,
 starting at position <TT>n</TT>.
</UL>

<PRE><BR># <B>let</B><CODE> </CODE>suffix<CODE> </CODE>s<CODE> </CODE>i<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>try</B><CODE> </CODE>String.sub<CODE> </CODE>s<CODE> </CODE>i<CODE> </CODE><TT>(</TT><TT>(</TT>String.length<CODE> </CODE>s<TT>)</TT><CODE>-</CODE>i<TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Invalid_argument<TT>(</TT><CODE>"String.sub"</CODE><TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE><CODE>""</CODE><CODE> </CODE>;;<BR><CODE>val suffix : string -&gt; int -&gt; string = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>split<CODE> </CODE>c<CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><B>rec</B><CODE> </CODE>split_from<CODE> </CODE>n<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>p<CODE> </CODE><CODE>=</CODE><CODE> </CODE>String.index_from<CODE> </CODE>s<CODE> </CODE>n<CODE> </CODE>c<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><TT>(</TT>String.sub<CODE> </CODE>s<CODE> </CODE>n<CODE> </CODE><TT>(</TT>p<CODE>-</CODE>n<TT>)</TT><TT>)</TT><CODE> </CODE>::<CODE> </CODE><TT>(</TT>split_from<CODE> </CODE><TT>(</TT>p<CODE>+</CODE><CODE>1</CODE><TT>)</TT><TT>)</TT><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Not_found<CODE> </CODE>-&gt;<CODE> </CODE><CODE>[</CODE><CODE> </CODE>suffix<CODE> </CODE>s<CODE> </CODE>n<CODE> </CODE><CODE>]</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>if</B><CODE> </CODE>s<CODE>=</CODE><CODE>""</CODE><CODE> </CODE><B>then</B><CODE> </CODE>[]<CODE> </CODE><B>else</B><CODE> </CODE>split_from<CODE> </CODE><CODE>0</CODE><CODE> </CODE>;;<BR><CODE>val split : char -&gt; string -&gt; string list = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
The only remarkable characteristic in this implementation is the use
of exceptions, specifically the exception <TT>Not_found</TT>.<BR>
<BR>

<H5> Computing the <I>data_base</I> structure</H5><A NAME="base_ex"></A>
<A NAME="@fonctions182"></A>
There is no difficulty in creating an array of strings from a list of
strings, since this is what the <TT>of_list</TT> function in the
<TT>Array</TT> module does. It might seem more complicated to compute
the index function from a list of field names, but the <TT>List</TT>
module provides all the needed tools.<BR>
<BR>
Starting from a list of strings, we need to code a function that
associates each string with an index corresponding to its position in
the list.
<A NAME="@fonctions183"></A>
<A NAME="@fonctions184"></A>


<PRE><BR># <B>let</B><CODE> </CODE>mk_index<CODE> </CODE>list_names<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><B>rec</B><CODE> </CODE>make_enum<CODE> </CODE>a<CODE> </CODE>b<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>if</B><CODE> </CODE>a<CODE> </CODE><CODE>&gt;</CODE><CODE> </CODE>b<CODE> </CODE><B>then</B><CODE> </CODE>[]<CODE> </CODE><B>else</B><CODE> </CODE>a::<TT>(</TT>make_enum<CODE> </CODE><TT>(</TT>a<CODE>+</CODE><CODE>1</CODE><TT>)</TT><CODE> </CODE>b<TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>list_index<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE><TT>(</TT>make_enum<CODE> </CODE><CODE>0</CODE><CODE> </CODE><TT>(</TT><TT>(</TT>List.length<CODE> </CODE>list_names<TT>)</TT><CODE> </CODE><CODE>-</CODE><CODE> </CODE><CODE>1</CODE><TT>)</TT><TT>)</TT><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>assoc_index_name<CODE> </CODE><CODE>=</CODE><CODE> </CODE>List.combine<CODE> </CODE>list_names<CODE> </CODE>list_index<CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>function</B><CODE> </CODE>name<CODE> </CODE>-&gt;<CODE> </CODE>List.assoc<CODE> </CODE>name<CODE> </CODE>assoc_index_name<CODE> </CODE><CODE> </CODE>;;<BR><CODE>val mk_index : 'a list -&gt; 'a -&gt; int = &lt;fun&gt;</CODE><BR>

</PRE>

To create the association function between field names and indexes, we
combine the list of indexes and the list of names to obtain a
list of associations of the type <I>string&nbsp;*&nbsp;int&nbsp;list</I>. To look up
the index associated with a name, we use the function <TT>assoc</TT>
from the <TT>List</TT> library. The function <TT>mk_index</TT>
returns a function that takes a name and calls <TT>assoc</TT> on this
name and the previously built association list.<BR>
<BR>
It is now possible to create a function that reads a file of
the given format.


<PRE><BR># <B>let</B><CODE> </CODE>read_base<CODE> </CODE>filename<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>channel<CODE> </CODE><CODE>=</CODE><CODE> </CODE>open_in<CODE> </CODE>filename<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>split_line<CODE> </CODE><CODE>=</CODE><CODE> </CODE>split<CODE> </CODE><CODE>':'</CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>list_names<CODE> </CODE><CODE>=</CODE><CODE> </CODE>split<CODE> </CODE><CODE>'|'</CODE><CODE> </CODE><TT>(</TT>input_line<CODE> </CODE>channel<TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE><B>rec</B><CODE> </CODE>read_file<CODE> </CODE>()<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>data<CODE> </CODE><CODE>=</CODE><CODE> </CODE>Array.of_list<CODE> </CODE><TT>(</TT>split_line<CODE> </CODE><TT>(</TT>input_line<CODE> </CODE>channel<CODE> </CODE><TT>)</TT><TT>)</TT><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>data<CODE> </CODE>::<CODE> </CODE><TT>(</TT>read_file<CODE> </CODE>()<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>End_of_file<CODE> </CODE>-&gt;<CODE> </CODE><CODE> </CODE>close_in<CODE> </CODE>channel<CODE> </CODE>;<CODE> </CODE>[]<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>{<CODE> </CODE>card_index<CODE> </CODE><CODE>=</CODE><CODE> </CODE>mk_index<CODE> </CODE>list_names<CODE> </CODE>;<CODE> </CODE>data<CODE> </CODE><CODE>=</CODE><CODE> </CODE>read_file<CODE> </CODE>()<CODE> </CODE>}<CODE> </CODE>;;<BR><CODE>val read_base : string -&gt; data_base = &lt;fun&gt;</CODE><BR>

</PRE>

The auxiliary function <TT>read_file</TT> reads records from the
file, and works recursively on the input channel. The base case of
the recursion corresponds to the end of the file, signaled by the
<TT>End_of_file</TT> exception. In this case, the empty list is
returned after closing the channel.<BR>
<BR>
The association's file can now be loaded:


<PRE><BR># <B>let</B><CODE> </CODE>base_ex<CODE> </CODE><CODE>=</CODE><CODE> </CODE>read_base<CODE> </CODE><CODE>"association.dat"</CODE><CODE> </CODE>;;<BR><CODE>val base_ex : data_base =</CODE><BR><CODE>  {card_index=&lt;fun&gt;;</CODE><BR><CODE>   data=</CODE><BR><CODE>    [[|"0"; "Chailloux"; "Emmanuel"; "Universit\233 P6"; "0144274427";</CODE><BR><CODE>       "ec@lip6.fr"; "email"; "25.12.1998"; "100.00"|];</CODE><BR><CODE>     [|"1"; "Manoury"; "Pascal"; "Laboratoire PPS"; ...|]; ...]}</CODE><BR>

</PRE>
<BR>
<BR>
<A NAME="toc74"></A>
<H3> General principles for database processing</H3>
The effectiveness and difficulty of processing the data in a
database is proportional to the power and complexity of the query
language. Since we want to use Objective CAML as query language, there is no
limit a priori on the requests we can express! However, we
also want to provide some simple tools to manipulate cards and their
data. This desire for simplicity requires us to limit the power of the 
Objective CAML
language, through the use of general goals and principles for database
processing.<BR>
<BR>
The goal of database processing is to obtain a state of the
database. Building such a state may be decomposed into three
steps:
<OL type=1>
<LI>
 selecting, according to some given criterion, a set of
 cards;

<LI> processing each of the selected cards;

<LI> processing all the data collected on the cards.
</OL>
Figure&nbsp;<A HREF="book-ora057.html#fig-gestassoc-requete">6.1</A> illustrates this decomposition.
<BLOCKQUOTE><DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV>
<DIV ALIGN=center>
<IMG SRC="book-ora021.gif">
</DIV>
<BR>
<DIV ALIGN=center>Figure 6.1: Processing a request.</DIV><BR>

<A NAME="fig-gestassoc-requete"></A>
<DIV ALIGN=center><HR WIDTH="80%" SIZE=2></DIV></BLOCKQUOTE>According to this decomposition, we need three functions of the
following types:
<OL type=1>
<LI>
 <I>(data_card -&gt; bool) -&gt; data_card list -&gt; data_card list</I>

<LI> <I>(data_card -&gt; 'a) -&gt; data_card list -&gt; 'a list</I>

<LI> <I>('a -&gt; 'b -&gt; 'b) -&gt; 'a list -&gt; 'b -&gt; 'b</I>
</OL>
Objective CAML provides us with three higher-order function, also known as
iterators, introduced page <A HREF="book-ora076.html#fn-fold">??</A>, that satisfy our
specification:


<PRE><BR># List.find_all<CODE> </CODE>;;<BR><CODE>- : ('a -&gt; bool) -&gt; 'a list -&gt; 'a list = &lt;fun&gt;</CODE><BR># List.map<CODE> </CODE>;;<BR><CODE>- : ('a -&gt; 'b) -&gt; 'a list -&gt; 'b list = &lt;fun&gt;</CODE><BR># List.fold_right<CODE> </CODE>;;<BR><CODE>- : ('a -&gt; 'b -&gt; 'b) -&gt; 'a list -&gt; 'b -&gt; 'b = &lt;fun&gt;</CODE><BR>

</PRE>

We will be able to use them to implement the three steps of building a
state by choosing the functions they take as an argument.<BR>
<BR>
For some special requests, we will also use:


<PRE><BR># List.iter<CODE> </CODE>;;<BR><CODE>- : ('a -&gt; unit) -&gt; 'a list -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>

Indeed, if the required processing consists only of displaying some
data, there is nothing to compute.<BR>
<BR>
In the next paragraphs, we are going to see how to define functions
expressing simple selection criteria, as well as simple queries. We
conclude this section with a short example using these functions
according to the principles stated above.<BR>
<BR>
<A NAME="toc75"></A>
<H3> Selection criteria</H3>
Concretely, the boolean function corresponding to the selection
criterion of a card is a boolean combination of properties of some or
all of the fields of the card. Each field of a card, even though it
is a string, can contain some information of another type: a float, a
date, etc.<BR>
<BR>

<H4> Selection criteria on a field</H4>
Selecting on some field is usually done using a function of the type
<I>data_base -&gt; 'a -&gt; string -&gt; data_card -&gt; bool</I>. The
<I>'a</I> type parameter corresponds to the type of the information
contained in the field. The <I>string</I> argument corresponds to
the name of the field.<BR>
<BR>

<H5> String fields</H5>
We define two simple tests on strings: equality with another string,
and non-emptiness.


<PRE><BR># <B>let</B><CODE> </CODE>eq_sfield<CODE> </CODE>db<CODE> </CODE>s<CODE> </CODE>n<CODE> </CODE>dc<CODE> </CODE><CODE>=</CODE><CODE> </CODE><CODE> </CODE><TT>(</TT>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT>field<CODE> </CODE>db<CODE> </CODE>n<CODE> </CODE>dc<TT>)</TT><TT>)</TT><CODE> </CODE>;;<BR><CODE>val eq_sfield : data_base -&gt; string -&gt; string -&gt; data_card -&gt; bool = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>nonempty_sfield<CODE> </CODE>db<CODE> </CODE>n<CODE> </CODE>dc<CODE> </CODE><CODE>=</CODE><CODE> </CODE><TT>(</TT><CODE>""</CODE><CODE> </CODE><CODE>&lt;&gt;</CODE><CODE> </CODE><TT>(</TT>field<CODE> </CODE>db<CODE> </CODE>n<CODE> </CODE>dc<TT>)</TT><TT>)</TT><CODE> </CODE>;;<BR><CODE>val nonempty_sfield : data_base -&gt; string -&gt; data_card -&gt; bool = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>

<H5> Float fields</H5>
To implement tests on data of type float, it is enough to translate
the <I>string</I> representation of a decimal number into its
<I>float</I> value. Here are some examples obtained from a generic
function
<TT>tst_ffield</TT>: 


<PRE><BR># <B>let</B><CODE> </CODE>tst_ffield<CODE> </CODE>r<CODE> </CODE>db<CODE> </CODE>v<CODE> </CODE>n<CODE> </CODE>dc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>r<CODE> </CODE>v<CODE> </CODE><TT>(</TT>float_of_string<CODE> </CODE><TT>(</TT>field<CODE> </CODE>db<CODE> </CODE>n<CODE> </CODE>dc<TT>)</TT><TT>)</TT><CODE> </CODE>;;<BR><CODE>val tst_ffield :</CODE><BR><CODE>  ('a -&gt; float -&gt; 'b) -&gt; data_base -&gt; 'a -&gt; string -&gt; data_card -&gt; 'b = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>eq_ffield<CODE> </CODE><CODE>=</CODE><CODE> </CODE>tst_ffield<CODE> </CODE><TT>(</TT><CODE>=</CODE><TT>)</TT><CODE> </CODE>;;<CODE> </CODE><CODE> </CODE><BR># <B>let</B><CODE> </CODE>lt_ffield<CODE> </CODE><CODE>=</CODE><CODE> </CODE>tst_ffield<CODE> </CODE><TT>(</TT><CODE>&lt;</CODE><TT>)</TT><CODE> </CODE>;;<CODE> </CODE><CODE> </CODE><BR># <B>let</B><CODE> </CODE>le_ffield<CODE> </CODE><CODE>=</CODE><CODE> </CODE>tst_ffield<CODE> </CODE><TT>(</TT><CODE>&lt;=</CODE><TT>)</TT><CODE> </CODE>;;<CODE> </CODE><BR><CODE>(* etc. *)</CODE><CODE> </CODE><BR>

</PRE>

These three functions have type:<BR>
<BR>
<I>data_base -&gt; float -&gt; string -&gt; data_card -&gt; bool</I>.<BR>
<BR>

<H5> Dates</H5>
This kind of information is a little more complex to deal with, as it
depends on the representation format of dates, and requires that we define
date comparison.<BR>
<BR>
We decide to represent dates in a card as a string with format
<CODE>dd.mm.yyyy</CODE>. In order to be able to define additional
comparisons, we also allow the replacement of the day, month or year
part with the underscore character (<CODE>'_'</CODE>). Dates are
compared according to the lexicographic order of lists of integers of
the form <I>[year; month; day]</I>. To express queries such as: ``is
before July 1998'', we use the <I><FONT COLOR=maroon>date pattern</FONT></I>:
<EM>"_.07.1998"</EM>. Comparing a date with a pattern is
accomplished with the function <TT>tst_dfield</TT> which analyses the
pattern to create the <I>ad hoc</I> comparison function. To define this
generic test function on dates, we need a few auxiliary functions.<BR>
<BR>
We first code two conversion functions from dates
(<TT>ints_of_string</TT>) and date patterns
(<TT>ints_of_dpat</TT>) to lists of ints.
The character <CODE>'_'</CODE> of a pattern will be replaced by the integer
0:


<PRE><BR># <B>let</B><CODE> </CODE>split_date<CODE> </CODE><CODE>=</CODE><CODE> </CODE>split<CODE> </CODE><CODE>'.'</CODE><CODE> </CODE>;;<BR><CODE>val split_date : string -&gt; string list = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>ints_of_string<CODE> </CODE>d<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>try</B><CODE> </CODE><CODE> </CODE><B>match</B><CODE> </CODE>split_date<CODE> </CODE>d<CODE> </CODE><B>with</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>[</CODE>d;m;y<CODE>]</CODE><CODE> </CODE>-&gt;<CODE> </CODE><CODE>[</CODE>int_of_string<CODE> </CODE>y;<CODE> </CODE>int_of_string<CODE> </CODE>m;<CODE> </CODE>int_of_string<CODE> </CODE>d<CODE>]</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE>failwith<CODE> </CODE><CODE>"Bad date format"</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Failure<TT>(</TT><CODE>"int_of_string"</CODE><TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE>failwith<CODE> </CODE><CODE>"Bad date format"</CODE><CODE> </CODE>;;<BR><CODE>val ints_of_string : string -&gt; int list = &lt;fun&gt;</CODE><BR><BR># <B>let</B><CODE> </CODE>ints_of_dpat<CODE> </CODE>d<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>int_of_stringpat<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>function</B><CODE> </CODE><CODE>"_"</CODE><CODE> </CODE>-&gt;<CODE> </CODE><CODE>0</CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE>s<CODE> </CODE>-&gt;<CODE> </CODE>int_of_string<CODE> </CODE>s<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>in</B><CODE> </CODE><B>try</B><CODE> </CODE><CODE> </CODE><B>match</B><CODE> </CODE>split_date<CODE> </CODE>d<CODE> </CODE><B>with</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>[</CODE>d;m;y<CODE>]</CODE><CODE> </CODE>-&gt;<CODE> </CODE><CODE>[</CODE><CODE> </CODE>int_of_stringpat<CODE> </CODE>y;<CODE> </CODE>int_of_stringpat<CODE> </CODE>m;<CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>int_of_stringpat<CODE> </CODE>d<CODE> </CODE><CODE>]</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE>-&gt;<CODE> </CODE>failwith<CODE> </CODE><CODE>"Bad date format"</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>with</B><CODE> </CODE>Failure<TT>(</TT><CODE>"int_of_string"</CODE><TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE>failwith<CODE> </CODE><CODE>"Bad date pattern"</CODE><CODE> </CODE>;;<BR><CODE>val ints_of_dpat : string -&gt; int list = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
Given a relation <TT>r</TT> on integers, we now code the test function.
It simply consists of implementing the lexicographic order, taking
into account the particular case of 0:


<PRE><BR># <B>let</B><CODE> </CODE><B>rec</B><CODE> </CODE>app_dtst<CODE> </CODE>r<CODE> </CODE>d1<CODE> </CODE>d2<CODE> </CODE><CODE>=</CODE><CODE> </CODE><B>match</B><CODE> </CODE>d1<CODE>,</CODE><CODE> </CODE>d2<CODE> </CODE><B>with</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>[]<CODE> </CODE><CODE>,</CODE><CODE> </CODE>[]<CODE> </CODE>-&gt;<CODE> </CODE><B>false</B><BR><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><TT>(</TT><CODE>0</CODE>::d1<TT>)</TT><CODE> </CODE><CODE>,</CODE><CODE> </CODE><TT>(</TT><CODE>_::</CODE>d2<TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE>app_dtst<CODE> </CODE>r<CODE> </CODE>d1<CODE> </CODE>d2<BR><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><TT>(</TT>n1::d1<TT>)</TT><CODE> </CODE><CODE>,</CODE><CODE> </CODE><TT>(</TT>n2::d2<TT>)</TT><CODE> </CODE>-&gt;<CODE> </CODE><TT>(</TT>r<CODE> </CODE>n1<CODE> </CODE>n2<TT>)</TT><CODE> </CODE><CODE>||</CODE><CODE> </CODE><TT>(</TT><TT>(</TT>n1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>n2<TT>)</TT><CODE> </CODE><CODE>&amp;&amp;</CODE><CODE> </CODE><TT>(</TT>app_dtst<CODE> </CODE>r<CODE> </CODE>d1<CODE> </CODE>d2<TT>)</TT><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_,</CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE>-&gt;<CODE> </CODE>failwith<CODE> </CODE><CODE>"Bad date pattern or format"</CODE><CODE> </CODE>;;<BR><CODE>val app_dtst : (int -&gt; int -&gt; bool) -&gt; int list -&gt; int list -&gt; bool = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We finally define the generic function <TT>tst_dfield</TT> which
takes as arguments a relation <TT>r</TT>, a database <TT>db</TT>, a
pattern <TT>dp</TT>, a field name <TT>nm</TT>, and a card
<TT>dc</TT>. This function checks that the pattern and the field from
the card satisfy the relation.


<PRE><BR># <B>let</B><CODE> </CODE>tst_dfield<CODE> </CODE>r<CODE> </CODE>db<CODE> </CODE>dp<CODE> </CODE>nm<CODE> </CODE>dc<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE>r<CODE> </CODE><TT>(</TT>ints_of_dpat<CODE> </CODE>dp<TT>)</TT><CODE> </CODE><TT>(</TT>ints_of_string<CODE> </CODE><TT>(</TT>field<CODE> </CODE>db<CODE> </CODE>nm<CODE> </CODE>dc<TT>)</TT><TT>)</TT><CODE> </CODE>;;<BR><CODE>val tst_dfield :</CODE><BR><CODE>  (int list -&gt; int list -&gt; 'a) -&gt;</CODE><BR><CODE>  data_base -&gt; string -&gt; string -&gt; data_card -&gt; 'a = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We now apply it to three relations.


<PRE><BR># <B>let</B><CODE> </CODE>eq_dfield<CODE> </CODE><CODE>=</CODE><CODE> </CODE>tst_dfield<CODE> </CODE><TT>(</TT><CODE>=</CODE><TT>)</TT><CODE> </CODE>;;<BR># <B>let</B><CODE> </CODE>le_dfield<CODE> </CODE><CODE>=</CODE><CODE> </CODE>tst_dfield<CODE> </CODE><TT>(</TT><CODE>&lt;=</CODE><TT>)</TT><CODE> </CODE>;;<BR># <B>let</B><CODE> </CODE>ge_dfield<CODE> </CODE><CODE>=</CODE><CODE> </CODE>tst_dfield<CODE> </CODE><TT>(</TT><CODE>&gt;=</CODE><TT>)</TT><CODE> </CODE>;;<BR>

</PRE>

These three functions have type: <BR><I>data_base -&gt; string -&gt; string -&gt; data_card -&gt; bool</I>.<BR>
<BR>

<H4> Composing criteria</H4>
The tests we have defined above all take as first arguments a
database, a value, and the name of a field. When we write a query, the
value of these three arguments are known. For instance, when we work
on the database <TT>base_ex</TT>, the test ``is before July 1998''
is written


<PRE><BR># ge_dfield<CODE> </CODE>base_ex<CODE> </CODE><CODE>"_.07.1998"</CODE><CODE> </CODE><CODE>"Date"</CODE><CODE> </CODE>;;<BR><CODE>- : data_card -&gt; bool = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
Thus, we can consider a test as a function of type <I>data_card
 -&gt; bool</I>. We want to obtain boolean combinations of the results of
such functions applied to a given card. To this end, we implement the
iterator:


<PRE><BR># <B>let</B><CODE> </CODE>fold_funs<CODE> </CODE>b<CODE> </CODE>c<CODE> </CODE>fs<CODE> </CODE>dc<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE>List.fold_right<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>f<CODE> </CODE>-&gt;<CODE> </CODE><B>fun</B><CODE> </CODE>r<CODE> </CODE>-&gt;<CODE> </CODE>c<CODE> </CODE><TT>(</TT>f<CODE> </CODE>dc<TT>)</TT><CODE> </CODE>r<TT>)</TT><CODE> </CODE>fs<CODE> </CODE>b<CODE> </CODE>;;<BR><CODE>val fold_funs : 'a -&gt; ('b -&gt; 'a -&gt; 'a) -&gt; ('c -&gt; 'b) list -&gt; 'c -&gt; 'a = &lt;fun&gt;</CODE><BR>

</PRE>

Where <TT>b</TT> is the base value, the function <TT>c</TT> is the
boolean operator, <TT>fs</TT> is the list of test functions on a
field, and <TT>dc</TT> is a card.<BR>
<BR>
We can obtain the conjunction and the disjunction of a list of tests with:


<PRE><BR># <B>let</B><CODE> </CODE>and_fold<CODE> </CODE>fs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>fold_funs<CODE> </CODE><B>true</B><CODE> </CODE><TT>(</TT><CODE>&amp;</CODE><TT>)</TT><CODE> </CODE>fs<CODE> </CODE>;;<BR><CODE>val and_fold : ('a -&gt; bool) list -&gt; 'a -&gt; bool = &lt;fun&gt;</CODE><BR># <B>let</B><CODE> </CODE>or_fold<CODE> </CODE>fs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>fold_funs<CODE> </CODE><B>false</B><CODE> </CODE><TT>(</TT><B>or</B><TT>)</TT><CODE> </CODE>fs<CODE> </CODE>;;<BR><CODE>val or_fold : ('a -&gt; bool) list -&gt; 'a -&gt; bool = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We easily define the negation of a test:


<PRE><BR># <B>let</B><CODE> </CODE>not_fun<CODE> </CODE>f<CODE> </CODE>dc<CODE> </CODE><CODE>=</CODE><CODE> </CODE>not<CODE> </CODE><TT>(</TT>f<CODE> </CODE>dc<TT>)</TT><CODE> </CODE>;;<BR><CODE>val not_fun : ('a -&gt; bool) -&gt; 'a -&gt; bool = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
For instance, we can use these combinators to define a selection
function for cards whose date field is included in a given range:


<PRE><BR># <B>let</B><CODE> </CODE>date_interval<CODE> </CODE>db<CODE> </CODE>d1<CODE> </CODE>d2<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE>and_fold<CODE> </CODE><CODE>[</CODE><TT>(</TT>le_dfield<CODE> </CODE>db<CODE> </CODE>d1<CODE> </CODE><CODE>"Date"</CODE><TT>)</TT>;<CODE> </CODE><TT>(</TT>ge_dfield<CODE> </CODE>db<CODE> </CODE>d2<CODE> </CODE><CODE>"Date"</CODE><TT>)</TT><CODE>]</CODE><CODE> </CODE>;;<BR><CODE>val date_interval : data_base -&gt; string -&gt; string -&gt; data_card -&gt; bool =</CODE><BR><CODE>  &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
<A NAME="toc76"></A>
<H3> Processing and computation</H3>
It is difficult to guess how a card might be processed, or the data that
would result from that processing. Nevertheless, we can consider two
common cases: numerical computation and data formatting for printing.
Let's take an example for each of these two cases.<BR>
<BR>

<H4> Data formatting</H4>
In order to print, we wish to create a string containing the name
of a member of the association, followed by some information.<BR>
<BR>
We start with a function that reverses the splitting of a line
using a given separating character:


<PRE><BR># <B>let</B><CODE> </CODE>format_list<CODE> </CODE>c<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>s<CODE> </CODE><CODE>=</CODE><CODE> </CODE>String.make<CODE> </CODE><CODE>1</CODE><CODE> </CODE>c<CODE> </CODE><B>in</B><CODE> </CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE>List.fold_left<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>x<CODE> </CODE>y<CODE> </CODE>-&gt;<CODE> </CODE><B>if</B><CODE> </CODE>x<CODE>=</CODE><CODE>""</CODE><CODE> </CODE><B>then</B><CODE> </CODE>y<CODE> </CODE><B>else</B><CODE> </CODE>x<CODE>^</CODE>s<CODE>^</CODE>y<TT>)</TT><CODE> </CODE><CODE>""</CODE><CODE> </CODE>;;<BR><CODE>val format_list : char -&gt; string list -&gt; string = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
In order to build the list of fields we are interested in, we code the
function <TT>extract</TT> that returns the fields associated
with a given list of names in a given card:


<PRE><BR># <B>let</B><CODE> </CODE>extract<CODE> </CODE>db<CODE> </CODE>ns<CODE> </CODE>dc<CODE> </CODE><CODE>=</CODE><CODE> </CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>List.map<CODE> </CODE><TT>(</TT><B>fun</B><CODE> </CODE>n<CODE> </CODE>-&gt;<CODE> </CODE>field<CODE> </CODE>db<CODE> </CODE>n<CODE> </CODE>dc<TT>)</TT><CODE> </CODE>ns<CODE> </CODE><CODE> </CODE>;;<BR><CODE>val extract : data_base -&gt; string list -&gt; data_card -&gt; string list = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
We can now write the line formatting function:


<PRE><BR># <B>let</B><CODE> </CODE>format_line<CODE> </CODE>db<CODE> </CODE>ns<CODE> </CODE>dc<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><TT>(</TT>String.uppercase<CODE> </CODE><TT>(</TT>field<CODE> </CODE>db<CODE> </CODE><CODE>"Lastname"</CODE><CODE> </CODE>dc<TT>)</TT><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE>^</CODE><CODE>" "</CODE><CODE>^</CODE><TT>(</TT>field<CODE> </CODE>db<CODE> </CODE><CODE>"Firstname"</CODE><CODE> </CODE>dc<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE>^</CODE><CODE>"\t"</CODE><CODE>^</CODE><TT>(</TT>format_list<CODE> </CODE><CODE>'\t'</CODE><CODE> </CODE><TT>(</TT>extract<CODE> </CODE>db<CODE> </CODE>ns<CODE> </CODE>dc<TT>)</TT><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE>^</CODE><CODE>"\n"</CODE><CODE> </CODE>;;<BR><CODE>val format_line : data_base -&gt; string list -&gt; data_card -&gt; string = &lt;fun&gt;</CODE><BR>

</PRE>

The argument <TT>ns</TT> is the list of requested fields. In the
resulting string, fields are separated by a tab (<CODE>'\t'</CODE>)
and the string is terminated with a newline character.<BR>
<BR>
We display the list of last and first names of all members with:


<PRE><BR># List.iter<CODE> </CODE>print_string<CODE> </CODE><TT>(</TT>List.map<CODE> </CODE><TT>(</TT>format_line<CODE> </CODE>base_ex<CODE> </CODE>[]<TT>)</TT><CODE> </CODE>base_ex<CODE>.</CODE>data<TT>)</TT><CODE> </CODE>;;<BR><CODE>CHAILLOUX Emmanuel	</CODE><BR><CODE>MANOURY Pascal	</CODE><BR><CODE>PAGANO Bruno	</CODE><BR><CODE>BARO Sylvain	</CODE><BR><CODE>- : unit = ()</CODE><BR>

</PRE>
<BR>
<BR>

<H4> Numerical computation</H4>
We want to compute the total amount of received fees for a given set
of cards. This is easily done by composing the extraction and
conversion of the correct field with the addition. To get nicer
code, we define an infix composition operator:


<PRE><BR># <B>let</B><CODE> </CODE><TT>(</TT><CODE>++</CODE><TT>)</TT><CODE> </CODE>f<CODE> </CODE>g<CODE> </CODE>x<CODE> </CODE><CODE>=</CODE><CODE> </CODE>g<CODE> </CODE><TT>(</TT>f<CODE> </CODE>x<TT>)</TT><CODE> </CODE>;;<BR><CODE>val ++ : ('a -&gt; 'b) -&gt; ('b -&gt; 'c) -&gt; 'a -&gt; 'c = &lt;fun&gt;</CODE><BR>

</PRE>

We use this operator in the following definition:


<PRE><BR># <B>let</B><CODE> </CODE>total<CODE> </CODE>db<CODE> </CODE>dcs<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>List.fold_right<CODE> </CODE><TT>(</TT><TT>(</TT>field<CODE> </CODE>db<CODE> </CODE><CODE>"Amount"</CODE><TT>)</TT><CODE> </CODE><CODE>++</CODE><CODE> </CODE>float_of_string<CODE> </CODE><CODE>++</CODE><CODE> </CODE><TT>(</TT><CODE>+.</CODE><TT>)</TT><TT>)</TT><CODE> </CODE>dcs<CODE> </CODE><CODE>0</CODE><CODE>.</CODE><CODE>0</CODE><CODE> </CODE>;;<BR><CODE>val total : data_base -&gt; data_card list -&gt; float = &lt;fun&gt;</CODE><BR>

</PRE>

We can now apply it to the whole database:


<PRE><BR># total<CODE> </CODE>base_ex<CODE> </CODE>base_ex<CODE>.</CODE>data<CODE> </CODE>;;<BR><CODE>- : float = 450</CODE><BR>

</PRE>
<BR>
<BR>
<A NAME="toc77"></A>
<H3> An example</H3>
To conclude, here is a small example of an application that uses the
principles described in the paragraphs above.<BR>
<BR>
We expect two kinds of queries on our database:
<UL>
<LI>
 a query returning two lists, the elements of the first 
containing the name of a member followed by his mail address, 
the elements of the other containing the name of the member 
followed by his email address, according to his preferences.

<LI> another query returning the state of received fees for a given
period of time. This state is composed of the list of last and
first names, dates and amounts of the fees as well as the total amount
of the received fees.
</UL>
<H4> List of addresses</H4><A NAME="sec-listes-adresses"></A> 
To create these lists, we first select the relevant cards according to
the field <CODE>"Pref"</CODE>, then we use the formatting function
<TT>format_line</TT>:


<PRE><BR># <B>let</B><CODE> </CODE>mail_addresses<CODE> </CODE>db<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>dcs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>List.find_all<CODE> </CODE><TT>(</TT>eq_sfield<CODE> </CODE>db<CODE> </CODE><CODE>"mail"</CODE><CODE> </CODE><CODE>"Pref"</CODE><TT>)</TT><CODE> </CODE>db<CODE>.</CODE>data<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>List.map<CODE> </CODE><TT>(</TT>format_line<CODE> </CODE>db<CODE> </CODE><CODE>[</CODE><CODE>"Mail"</CODE><CODE>]</CODE><TT>)</TT><CODE> </CODE>dcs<CODE> </CODE>;;<BR><CODE>val mail_addresses : data_base -&gt; string list = &lt;fun&gt;</CODE><BR><BR># <B>let</B><CODE> </CODE>email_addresses<CODE> </CODE>db<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>dcs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>List.find_all<CODE> </CODE><TT>(</TT>eq_sfield<CODE> </CODE>db<CODE> </CODE><CODE>"email"</CODE><CODE> </CODE><CODE>"Pref"</CODE><TT>)</TT><CODE> </CODE>db<CODE>.</CODE>data<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>List.map<CODE> </CODE><TT>(</TT>format_line<CODE> </CODE>db<CODE> </CODE><CODE>[</CODE><CODE>"Email"</CODE><CODE>]</CODE><TT>)</TT><CODE> </CODE>dcs<CODE> </CODE>;;<BR><CODE>val email_addresses : data_base -&gt; string list = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>

<H4> State of received fees</H4>
Computing the state of the received fees uses the same technique:
selection then processing. In this case however the processing part is
twofold: line formatting followed by the computation of the total
amount.


<PRE><BR># <B>let</B><CODE> </CODE>fees_state<CODE> </CODE>db<CODE> </CODE>d1<CODE> </CODE>d2<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>dcs<CODE> </CODE><CODE>=</CODE><CODE> </CODE>List.find_all<CODE> </CODE><TT>(</TT>date_interval<CODE> </CODE>db<CODE> </CODE>d1<CODE> </CODE>d2<TT>)</TT><CODE> </CODE>db<CODE>.</CODE>data<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>ls<CODE> </CODE><CODE>=</CODE><CODE> </CODE>List.map<CODE> </CODE><TT>(</TT>format_line<CODE> </CODE>db<CODE> </CODE><CODE>[</CODE><CODE>"Date"</CODE>;<CODE>"Amount"</CODE><CODE>]</CODE><TT>)</TT><CODE> </CODE>dcs<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>t<CODE> </CODE><CODE>=</CODE><CODE> </CODE>total<CODE> </CODE>db<CODE> </CODE>dcs<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>ls<CODE>,</CODE><CODE> </CODE>t<CODE> </CODE>;;<BR><CODE>val fees_state : data_base -&gt; string -&gt; string -&gt; string list * float = &lt;fun&gt;</CODE><BR>

</PRE>

The result of this query is a tuple containing a list of strings
with member information, and the total amount of received fees.<BR>
<BR>

<H4> Main program</H4> The main program is essentially an
interactive loop that displays the result of queries asked by the
user through a menu. We use here an imperative style, except for the
display of the results which uses an iterator.


<PRE><BR># <B>let</B><CODE> </CODE>main()<CODE> </CODE><CODE>=</CODE><BR><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>db<CODE> </CODE><CODE>=</CODE><CODE> </CODE>read_base<CODE> </CODE><CODE>"association.dat"</CODE><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>finished<CODE> </CODE><CODE>=</CODE><CODE> </CODE>ref<CODE> </CODE><B>false</B><CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>while</B><CODE> </CODE>not<CODE> </CODE><CODE>!</CODE>finished<CODE> </CODE><B>do</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_string<CODE>" 1: List of mail addresses\n"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_string<CODE>" 2: List of email addresses\n"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_string<CODE>" 3: Received fees\n"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_string<CODE>" 0: Exit\n"</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_string<CODE>"Your choice: "</CODE>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>match</B><CODE> </CODE>read_int()<CODE> </CODE><B>with</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>0</CODE><CODE> </CODE>-&gt;<CODE> </CODE>finished<CODE> </CODE><CODE>:=</CODE><CODE> </CODE><B>true</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>1</CODE><CODE> </CODE>-&gt;<CODE> </CODE><TT>(</TT>List.iter<CODE> </CODE>print_string<CODE> </CODE><TT>(</TT>mail_addresses<CODE> </CODE>db<TT>)</TT><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>2</CODE><CODE> </CODE>-&gt;<CODE> </CODE><TT>(</TT>List.iter<CODE> </CODE>print_string<CODE> </CODE><TT>(</TT>email_addresses<CODE> </CODE>db<TT>)</TT><TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>3</CODE><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>-&gt;<CODE> </CODE><TT>(</TT><B>let</B><CODE> </CODE>d1<CODE> </CODE><CODE>=</CODE><CODE> </CODE>print_string<CODE>"Start date: "</CODE>;<CODE> </CODE>read_line()<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>d2<CODE> </CODE><CODE>=</CODE><CODE> </CODE>print_string<CODE>"End date: "</CODE>;<CODE> </CODE>read_line()<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>let</B><CODE> </CODE>ls<CODE>,</CODE><CODE> </CODE>t<CODE> </CODE><CODE>=</CODE><CODE> </CODE>fees_state<CODE> </CODE>db<CODE> </CODE>d1<CODE> </CODE>d2<CODE> </CODE><B>in</B><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>List.iter<CODE> </CODE>print_string<CODE> </CODE>ls;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_string<CODE>"Total: "</CODE>;<CODE> </CODE>print_float<CODE> </CODE>t;<CODE> </CODE>print_newline()<TT>)</TT><BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE> </CODE><CODE>|</CODE><CODE> </CODE><CODE>_</CODE><CODE> </CODE>-&gt;<CODE> </CODE>()<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE><B>done</B>;<BR><CODE> </CODE><CODE> </CODE><CODE> </CODE>print_string<CODE>"bye\n"</CODE><CODE> </CODE>;;<BR><CODE>val main : unit -&gt; unit = &lt;fun&gt;</CODE><BR>

</PRE>
<BR>
<BR>
This example will be extended in chapter <A HREF="index.html#chap-PART4-Applications">21</A>
with an interface using a web browser.<BR>
<BR>
<A NAME="toc78"></A>
<H3> Further work</H3>
A natural extension of this example would consist of adding type
information to every field of the database. This information would be
used to define generic comparison operators with type
<I>data_base -&gt; 'a -&gt; string -&gt; data_card -&gt; bool</I>
where the name of the field (the third argument) would trigger the
correct conversion and test functions.<BR>
<BR>
<HR>
<A HREF="book-ora056.html"><IMG SRC ="previous_motif.gif" ALT="Previous"></A>
<A HREF="index.html"><IMG SRC ="contents_motif.gif" ALT="Contents"></A>
<A HREF="book-ora058.html"><IMG SRC ="next_motif.gif" ALT="Next"></A>
</BODY>
</HTML>
